/**
 ******************************************************************************
 * @file flags.c
 * @author GX (2524913364@qq.com)
 * @brief 	粤嵌GEC6818开发板LCD显示国旗，在调用本文件中的函数显示之前要先做好显存
 * 					映射。
 * 					
 * 					屏幕扫描方向：
 * 					○ x →
 *					y
 * 					↓
 * 
 * @version 2.1.1
 * @date 2022-08-16
 * 
 * @copyright Copyright (c) 2022
 * @note	此模块基于系统IO.
 * 				显示屏文件可以重复打开得到不同的文件描述符，而且都可以显存映射到不同的内存区，
 * 				并且对不同的映射内存写入内容都可以改变显示屏的内容。
 * 
 ******************************************************************************
 *----------------------------------------------------------------------------*
 *  Remark         : Description                                              *
 *----------------------------------------------------------------------------*
 *  Change History :                                                          *
 *  <Date>     | <Version> | <Author>       | <Description>                   *
 *----------------------------------------------------------------------------*
 *  2022-07-01 | 1.0.0     | GX             | Create file                     *
 *----------------------------------------------------------------------------*
 *  2022-07-02 | 1.0.1     | GX							| Rich functions                  * 
 *----------------------------------------------------------------------------*
 *  2022-07-09 | 2.0.0     | GX							| Support display characters,and	*
 * 						 |           |                | must add mode <fonts.h>					*
 *----------------------------------------------------------------------------* 
 *  2022-07-26 | 2.1.0     | GX             | add function:                   *  
 *             |           |                | lcd_show_bmp_rmColor()          *
 *             |           |                | lcd_show_buf()  								*
 * 						 |           |                | lcd_bmp_to_Buf()                *
 *----------------------------------------------------------------------------* 
 *  2022-08-16 | 2.1.1     | GX             | delete some show flag functions * 
 *             |           |                | add init function               *
 *----------------------------------------------------------------------------* 
 ******************************************************************************
 */

/*----------------------------- start of file -------------------------------*/
#ifndef _LCD_H
#define _LCD_H

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/fb.h>

#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdbool.h>
#include <stdint.h>

/** @defgroup 可选的模块，用户可以根据需要注释掉下面的宏实现裁剪
  * @{
  */
#define USE_FONTS       /* 使用字体库 */
#define SUPPORT_BMP     /* 支持显示bmp */
/**
  * @}
  */

#ifdef USE_FONTS
  #include "fonts.h"
#endif  /* USE_FONTS */


#ifdef  SUPPORT_BMP
// BMP格式头规范

/* 头信息 14个字节 */
typedef struct bitmap_header
{
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量
}bitmap_header;

/* 色位信息 40个字节 */
typedef struct bitmap_info
{
	int32_t size;   // 本结构大小	
	int32_t width;  // 图像宽
	int32_t height; // 图像高
	int16_t planes;

	int16_t bit_count; // 色深
	int32_t compression;
	int32_t size_img; // bmp数据大小，必须是4的整数倍
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
}bitmap_info;

// 以下结构体不一定存在于BMP文件中，除非：
// bitmap_info.compression为真
typedef struct rgb_quad
{
	int8_t blue;
	int8_t green;
	int8_t red;
	int8_t reserved;
}rgb_quad;

#endif  /* SUPPORT_BMP */

#define LCD_PATH        "/dev/fb0"              /* lcd设备文件路径 */

#define LCD_HEIGHT      480
#define LCD_WIDE        800
#define LINE            480
#define ROW             800
#define LCD_SIZE        (LCD_HEIGHT*LCD_WIDE)  /* 像素大小 */
#define LCD_BUFF_SIZE   (LCD_SIZE*4)           /* 800*480*4 byte */


/** @defgroup Common_colors 
  * @{
  */
#define LCD_RED         0x00FF0000    /* 红色 */
#define LCD_GREEN       0x0000FF00    /* 绿色 */
#define LCD_BLUE        0x000000FF    /* 蓝色 */
#define LCD_WHITE       0x00FFFFFF    /* 白色 */
#define LCD_BLACK       0x00000000    /* 黑色 */
#define LCD_YELLOW      0x00FFFF00    /* 黄色 */
#define LCD_PURPLE      0x00FF00FF    /* 紫色 */
#define LCD_CYAN        0x0000FFFF    /* 青色 */
/**
  * @}
  */



/** @defgroup define function
  * @{
  */
/**
 * @brief 单色铺满屏幕
 * @param fb    屏幕显存映射的基地址
 * @param color 用户选用颜色
 * 
 */
#define LCD_FULL(fb, color)    for(int i=0; i<( LCD_SIZE ); i++) \
                                { *((fb)+i) = (color); }

/**
 * @brief 清屏
 * @param fb    屏幕显存映射的基地址
 */
#define LCD_CLEAN(fb)             LCD_FULL((fb), LCD_WHITE);


/**
  * @}
  */



/*---------------------------------------------------------------------------*/
/*   A B C 区                                                                */
/*---------------------------------------------------------------------------*/

/** @defgroup display_block_each_ABC 
  * @{
  */
#define LCD_BLOCK       3       /* 虚拟显存的块数，用户可以修改选择，取值是1~3 */

/*---BLOCK A-----------------------------------------------------------------*/
#if LCD_BLOCK > 0
/**
 * @brief 将A区设为可见区
 * @param lcd   lcd文件描述符
 * @param vinfo 是个结构体指针 fb_var_screeninfo *
 */
#define LCD_SET_DISPLAY_A(lcd, vinfo)   (vinfo)->xoffset = 0; \
                                        (vinfo)->yoffset = 0; \
                                        ioctl((lcd), FBIOPAN_DISPLAY, (vinfo));
/* 将显存映射基地址移到A区 */                                        
#define LCD_DISPLAY_A(fd)               (fd)
#endif  /* LCD_BLOCK > 0 */

/*---BLOCK B-----------------------------------------------------------------*/
#if LCD_BLOCK > 1
/**
 * @brief 将B区设为可见区
 * @param lcd   lcd文件描述符
 * @param vinfo 是个结构体指针 fb_var_screeninfo *
 */
#define LCD_SET_DISPLAY_B(lcd, vinfo)   (vinfo)->xoffset = 0; \
                                        (vinfo)->yoffset = LCD_HEIGHT; \
                                        ioctl((lcd), FBIOPAN_DISPLAY, (vinfo));
/* 将显存映射基地址移到B区 */   
#define LCD_DISPLAY_B(fd)               (fd + LCD_SIZE)
#endif  /* LCD_BLOCK > 1 */

/*---BLOCK C-----------------------------------------------------------------*/
#if LCD_BLOCK > 2
/**
 * @brief 将C区设为可见区
 * @param lcd   lcd文件描述符
 * @param vinfo 是个结构体指针 fb_var_screeninfo *
 */
#define LCD_SET_DISPLAY_C(lcd, vinfo)   (vinfo)->xoffset = 0; \
                                        (vinfo)->yoffset = LCD_HEIGHT*2; \
                                        ioctl((lcd), FBIOPAN_DISPLAY, (vinfo));
/* 将显存映射基地址移到C区 */   
#define LCD_DISPLAY_C(fd)               (fd + LCD_SIZE*2)
#endif  /* LCD_BLOCK > 2 */

/*---BLOCK ABC---------------------------------------------------------------*/
/**
 * @brief 设置可见区
 * @param lcd   lcd文件描述符
 * @param vinfo 是个结构体指针 fb_var_screeninfo *
 * @param n     n%3 的值为 0 则在 A 区，为 1 则在 B 区，为 2 则在 C 区
 *              建议直接使用 lcd.c 定义好的全局变量 view_block
 */
#define LCD_SET_DISPLAY(lcd, vinfo, n)  (vinfo)->xoffset = 0; \
                                        (vinfo)->yoffset = LCD_HEIGHT*((n)%LCD_BLOCK); \
                                        ioctl((lcd), FBIOPAN_DISPLAY, (vinfo));
/**
 * @brief 将显存映射的基地址转化为 A 区 B 区或 C区
 * @param fb  屏幕显存映射的基地址
 * @param n   n%3 的值为 0 则在 A 区，为 1 则在 B 区，为 2 则在 C 区
 *            建议直接使用 lcd.c 定义好的全局变量 view_block
 */
#define LCD_DISPLAY(fb, n)              ((fb) + LCD_SIZE*((n)%LCD_BLOCK))

/**
  * @}
  */

/**
 * @brief 矩形色块结构体
 * 
 */
typedef struct Rectangle_Block
{
  int x;          /* 起始x坐标 */
  int y;          /* 起始y坐标 */
  int wide;       /* 宽度 */
  int height;     /* 高度 */
  int color;      /* 颜色 */
}Rectangle_Block;

extern uint32_t g_view_block;
extern int32_t g_fd_lcd;												/* lcd 文件描述符 */
extern int32_t *g_lcd_fb;											/* 显存映射的基地址 */
extern struct fb_var_screeninfo g_vinfo;        /* 显卡设备的可变属性结构体 */

/*---------------------------------------------------------------------------*/
/*   函数声明                                                                 */
/*---------------------------------------------------------------------------*/

/* 初始化lcd */
bool lcd_init();
bool lcd_close();

/* 显示一个现成的buf */
void lcd_show_buf(int32_t *fb_base, int32_t x, int y, int32_t wide ,int32_t height,
                   int32_t *buf, int32_t buf_width, int32_t buf_off_l, int32_t buf_off_r);

/* 绘制实心矩形 */
void lcd_rectangle(int32_t *fb_base, int32_t x, int32_t y, 
                    int32_t wide ,int32_t height, int32_t color);

/* 绘制实心圆 */
void lcd_circular(int32_t *fb_base, int32_t center_x, int32_t center_y, int32_t R, int32_t color);
/* 绘制圆环 */
void lcd_ring(int32_t *fb_base, int32_t center_x, int32_t center_y,
							 int32_t R_outer, int32_t R_inner, int32_t color);                    


#ifdef SUPPORT_BMP
bool lcd_show_bmp(int32_t *fb_base, const int8_t *bmp_name, int32_t x, int32_t y);
bool lcd_show_bmp_sticker(int32_t *fb_base, const int8_t *bmp_name, int32_t x, int32_t y);
bool lcd_show_bmp_rmColor(int32_t *fb_base, const int8_t *bmp_name, int32_t x, int32_t y,
                            int32_t rmcolor);
bool lcd_show_zipbmp(int32_t *fb_base, const int8_t *bmp_name, 
											int32_t x, int32_t y , uint32_t zip);
bool lcd_bmp_to_Buf (int32_t *des_buf, const int8_t *src_path, 
												uint32_t buf_width, uint32_t buf_height);     
#endif  /* SUPPORT_BMP */                                      


#ifdef USE_FONTS

void LCD_SetFont(sFONT *fonts);
sFONT *LCD_GetFont(void);
void LCD_SetColors(uint32_t TextColor, uint32_t BackColor, bool useBack); 
void LCD_GetColors(uint32_t *TextColor, uint32_t *BackColor, bool *useBack);
void LCD_SetTextColor(uint32_t Color);
void LCD_SetBackColor(uint32_t Color, bool use);
void DispChar_EN ( uint32_t *fb_base, uint16_t x, uint16_t y,
                   const int8_t cChar );
void DispString_EN ( uint32_t *fb_base, uint16_t x, uint16_t y,
                   const int8_t *str );
void DispStringLine_EN ( uint32_t *fb_base, uint16_t line, uint16_t nth,
                   const int8_t *str );       
void LCD_ClearLineBlock(uint32_t *fb_base, uint16_t line, 
													uint16_t from_nth, uint16_t to_nth);
void LCD_ClearLine(uint32_t *fb_base, uint16_t line);                          
#endif /* USE_FONTS */   

#endif /* _LCD_H */


/*------------------------------ end of file --------------------------------*/
